<template>
	<tm-sheet :transprent="props.transprent" :round="3" _class="flex flex-col overflow" :padding="props.padding" :margin="props.margin">
		<slot></slot>
	</tm-sheet>
</template>

<script lang="ts" setup>
/** 
 * 表单
 * @description 注意，内部需要放置tm-form-item,不限层级，可随意布局。
 * 对以下表单类组件进行字段收集。
 * 	"tm-radio-group","tm-checkbox-box",
    "tm-input","tm-rate","tm-slider",
    "tm-segtab","tm-switch","tm-upload"
 */
import { computed, PropType, provide, ref, watch, Ref, toRaw, shallowReadonly, nextTick, isProxy, unref, readonly, watchEffect } from 'vue'
import { formItem, validateResultListType } from './interface'
import tmSheet from '../tm-sheet/tm-sheet.vue'
import { validateFunCall, getObjectVal } from '../tm-form-item/validateFunCall'
/**
 * 事件说明
 * @method submit 提交表单时触发。
 * @method reset 重置表单时触发
 * @method validate 校验表单时触发
 * @method clearValidate 清除校验状态时触发。
 */
const emits = defineEmits<{
	(e: 'submit', event: Tmui.tmFormSubmitResult): void
	/**只要有数据更改就会触发校验并返回相关数据与submit相同 */
	(e: 'validate', event: Tmui.tmFormRules): void
	(e: 'reset'): void
	(e: 'clearValidate'): void
	(e: 'update:modelValue', event: any): void
}>()

const props = defineProps({
	modelValue: {
		type: Object as PropType<Object>,
		default: () => {
			return {}
		},
		required: true
	},
	margin: {
		type: Array as PropType<Array<number>>,
		default: () => [32, 24]
	},
	padding: {
		type: Array as PropType<Array<number>>,
		default: () => [16, 0]
	},
	//表单标签是竖还是横排列。
	//vertical,horizontal
	layout: {
		type: String as PropType<'vertical' | 'horizontal'>,
		default: 'horizontal'
	},
	//如果为0表示自动宽度。
	labelWidth: {
		type: Number,
		default: 160
	},
	//标签对齐方式
	labelAlign: {
		type: String,
		default: 'left'
	},
	//显示下划线。
	border: {
		type: Boolean,
		default: true
	},
	transprent: {
		type: Boolean,
		default: false
	}
})
const _modelVal = ref(props.modelValue)
//备份，重置时，使用。
const _backModelVal = uni.$tm.u.deepClone(props.modelValue)
watchEffect(() => {
	_modelVal.value = props.modelValue
})
//收集的字段。状态。它与_modelVal是有区别的，用户提供的字段，不一定就会在页面中存在，需要与已经渲染的字段进行匹配
const _callBackModelVal: Ref<Array<Tmui.tmFormRules>> = ref([])
const tmFormComnameId = 'tmFormId'
//允许被推送的组件清单类型.其它的组件不会被收集进行检验。
const safeFormCom = ref(['tm-radio-group', 'tm-checkbox-box', 'tm-input', 'tm-rate', 'tm-slider', 'tm-segtab', 'tm-switch', 'tm-upload'])
//需要对子级，响应的方法。
// 这里为了更好的性能不再使用vue2版本中children方式，而是采用了provide方式与父子间传递。
const formFunCallBack = ref('')
const validateResultList = ref<validateResultListType[]>([])
provide(
	'tmFormFun',
	computed(() => formFunCallBack.value)
)
provide(
	'tmFormLabelWidth',
	computed(() => props.labelWidth)
)
provide(
	'tmFormLabelAlign',
	computed(() => props.labelAlign)
)
provide(
	'tmFormLayout',
	computed(() => props.layout)
)
provide(
	'tmFormBorder',
	computed(() => props.border)
)
provide(
	'tmFormTransprent',
	computed(() => props.transprent)
)
provide(
	'formCallFiled',
	computed(() => _modelVal.value)
)
provide(
	'validateResultList',
	computed(() => validateResultList.value)
)
let timid: any = NaN
let ptimeId: any = NaN
watch(
	() => props.modelValue,
	() => {
		clearTimeout(timid)
		if (formFunCallBack.value == 'validate') {
			timid = setTimeout(function () {
				const result = validate()
				validateResultList.value = [...result.result]
			}, 100)
		}
	},
	{ deep: true }
)

function reset() {
	formFunCallBack.value = 'reset'
	let dblack = uni.$tm.u.deepClone(_backModelVal)
	emits('update:modelValue', dblack)
	emits('reset')
	_modelVal.value = dblack
}
function clearValidate() {
	formFunCallBack.value = 'clearValidate'
	nextTick(() => {
		emits('clearValidate')
	})
}
function submit() {
	formFunCallBack.value = 'validate'
	let isPass = true
	uni.$tm.u.throttle(
		() => {
			const result = validate()
			validateResultList.value = [...result.result]
			// @ts-ignore
			emits('submit', { data: toRaw(_modelVal.value), ...result })
		},
		220,
		false
	)
}
//执行表单检验
function validate() {
	formFunCallBack.value = 'validate'
	let par = toRaw(_callBackModelVal.value)
	let isPass = true
	let list: validateResultListType[] = []
	for (let i = 0, len = par.length; i < len; i++) {
		let item = par[i]
		let value = getObjectVal(_modelVal.value, item.field)
		const vallist = validateFunCall(item.rules, value)
		let rulstVal = {
			message: '校验通过',
			validator: true as Function | boolean
		}
		for (let j = 0; j < vallist.length; j++) {
			if (!vallist[j].validator) {
				isPass = false
				rulstVal.message = vallist[j]?.message ?? '校验通过'
				rulstVal.validator = vallist[j]?.validator ?? true
				break
			}
		}
		list.push({ field: item.field, ...rulstVal })
	}

	return { result: list, isPass, data: toRaw(_modelVal.value), validate: isPass }
}

function pushKey(item: formItem) {
	if (!item.field) return
	let idsIndex = _callBackModelVal.value.findIndex((el) => el.id == item.id)
	if (idsIndex == -1) {
		_callBackModelVal.value.push(item)
	} else {
		_callBackModelVal.value[idsIndex] = { ...item }
	}
}
function delKey(item: formItem) {
	let idsIndex = _callBackModelVal.value.findIndex((el) => el.id == item.id)
	if (idsIndex > -1) {
		_callBackModelVal.value.splice(idsIndex, 1)
	}
}
function setObjectVal(obj: any, field = '', val: any) {
	if (field == '') return obj
	var arr = field.split('.')
	while (arr.length > 1) {
		let key = String(arr.shift())
		obj = isProxy(obj[key]) ? toRaw(obj[key]) : obj[key]
	}
	return (obj[arr[0]] = isProxy(val) ? toRaw(val) : val)
}
/**
 * ref函数
 * @method submit 提交表单
 * @method reset 重置表单
 * @method validate 手动校验表单
 * @method clearValidate 清除校验状态
 */
defineExpose({
	reset,
	validate,
	clearValidate,
	submit,
	pushKey,
	delKey,
	tmFormComnameId
})
</script>

<style></style>
